package dream

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"gitee.com/dream_hat/dreamgo/hd"
	"net"
	"strings"
	"sync"
	"time"
	"unsafe"
)

func NewSocketPool(c string) IPool {
	p := NewPool(func(c interface{}) interface{} {
		var ssc string = ""
		switch v := c.(type) {
		case string:
			ssc = string(v)
			break
		}
		addrs := strings.Split(ssc, ",")
		v := &sSocket{
			headertemp:     make([]byte, 7),
			addr:           addrs[0],
			auth:           addrs[1],
			requestTimeout: 10,
			xym:            false,
		}
		return v
	}, c, 0xffffffff)
	return p
}

/*
ISocket 无状态连接
注：同步的
*/
type ISocket interface {
	SendPackage(xy XY, pack []byte) error
	ReadPackage(rxy ...XY) (XY, []byte, error)
	Request(xy XY, pack []byte, rxy ...XY) (XY, []byte, error)

	JSONSendPackage(xy XY, json map[string]interface{}) error
	JSONReadPackage(rxy ...XY) (XY, map[string]interface{}, error)
	JSONRequest(xy XY, json map[string]interface{}, rxy ...XY) (XY, map[string]interface{}, error)
}

type sSocket struct {
	con            net.Conn
	lock           *sync.Mutex
	requestTimeout uint32
	addr           string
	auth           string
	headertemp     []byte
	xym            bool
	key            string
}

func (p *sSocket) gethdlen() int {
	if p.xym {
		return 2
	} else {
		return 137
	}
}

func (p *sSocket) JSONSendPackage(xy XY, _json map[string]interface{}) error {
	res, _ := json.Marshal(&_json)
	return p.SendPackage(xy, res)
}
func (p *sSocket) JSONReadPackage(rxy ...XY) (xy XY, _json map[string]interface{}, err error) {
	var by []byte
	xy, by, err = p.ReadPackage(rxy...)
	if err != nil {
		return xy, nil, err
	}
	_json = make(map[string]interface{})
	json.Unmarshal(by, &_json)
	return xy, _json, err
}
func (p *sSocket) JSONRequest(xy XY, _json map[string]interface{}, rxy ...XY) (XY, map[string]interface{}, error) {
	err := p.JSONSendPackage(xy, _json)
	if err != nil {
		return 0, nil, err
	}
	return p.JSONReadPackage(rxy...)
}

func (p *sSocket) SendPackage(xy XY, pack []byte) error {
	doconnected := false
	if p.con == nil {
		p.connect()
		doconnected = true
	}
	if p.con == nil {
		return hd.E_BACK_CONNECT_ERROR
	}
	buf := allocPackWithInstall(true, xy, pack, p.gethdlen())
	err := loopSend_2019_12_26(p.con, buf, p.requestTimeout)
	if err != nil {
		Logi(LOG_KERL, "socket send_2019_12_26 fail ", err)
		p.con.Close()
		p.con = nil
		if doconnected {
			freePack(buf)
			return hd.E_BACK_SEND_ERROR
		}
		p.connect()
		if p.con == nil {
			freePack(buf)
			return hd.E_BACK_CONNECT_ERROR
		}
		err := loopSend(p.con, buf, p.requestTimeout)
		if err != nil {
			Logi(LOG_KERL, "socket resend fail ", err)
			freePack(buf)
			p.con.Close()
			p.con = nil
			return hd.E_BACK_SEND_ERROR
		}
	}
	freePack(buf)
	return nil
}

func (p *sSocket) ReadPackage(rxy ...XY) (XY, []byte, error) {
	if p.con == nil {
		return 0, nil, hd.E_BACK_RECV_STATE_ERROR
	}
	tlen, err := recvPackageHeader(p.con, p.gethdlen(), p.requestTimeout, p.headertemp)
	if err != nil {
		Logi(LOG_KERL, "socket recv len fail ", err)
		p.con.Close()
		p.con = nil
		return 0, nil, hd.E_BACK_RECV_ERROR
	}
	var xy XY = 0
	if true {
		if tlen < uint32(unsafe.Sizeof(xy)) {
			Logi(LOG_KERL, "socket recved len < xy ")
			p.con.Close()
			p.con = nil
			return 0, nil, hd.E_BACK_RECV_DATA_ERROR
		}
	}

	var pack []byte = nil
	if tlen > 0 {
		pack = allocPack(tlen)
		var err error
		if isValidHenlen(p.gethdlen()) {
			err = loopRecv(p.con, pack, p.requestTimeout)
		} else {
			var read int
			read, err = tryRecv(p.con, pack, p.requestTimeout)
			pack = pack[0:read]
		}
		if err != nil {
			Logi(LOG_KERL, "socket recv ", tlen, " data fail ", err)
			freePack(pack)
			p.con.Close()
			p.con = nil
			return 0, nil, hd.E_BACK_RECV_DATA_ERROR
		}
	}
	if true {
		if tlen < uint32(unsafe.Sizeof(xy)) {
			Logi(LOG_KERL, "socket recved len < xy ")
			if pack != nil {
				freePack(pack)
			}
			p.con.Close()
			p.con = nil
			return 0, nil, hd.E_BACK_RECV_DATA_ERROR
		}
		io := bytes.NewBuffer(pack)
		binary.Read(io, binary.LittleEndian, &xy)
	}
	if len(rxy) > 0 {
		if xy != rxy[0] {
			p.con.Close()
			p.con = nil
			Logi(LOG_KERL, "socket need XY:", int(rxy[0]), ",but recved XY:", int(xy))
			return xy, pack[int(unsafe.Sizeof(xy)):], hd.E_BACK_RECV_DATA_ERROR
		}
	}
	return xy, pack[int(unsafe.Sizeof(xy)):], nil
}

func (p *sSocket) Request(xy XY, pack []byte, rxy ...XY) (XY, []byte, error) {
	err := p.SendPackage(xy, pack)
	if err != nil {
		return 0, nil, err
	}
	return p.ReadPackage(rxy...)
}

func (p *sSocket) connect() {
	Assert(p.con == nil, "socket connect not nil")
	d := &net.Dialer{Timeout: time.Duration(p.requestTimeout) * time.Second}
	c, err := d.Dial("tcp", p.addr)
	if err != nil {
		Logi(LOG_KERL, " socket connect ", p.addr, " fail ", err)
		return
	}
	if p.xym {

	} else {
		j := NewJSON()
		j["type"] = 0
		j["id"] = 1
		j["sign"] = ""
		b, _ := json.Marshal(j)
		pk := allocPackWithInstall(true, 30001, b, p.gethdlen())
		var err = loopSend(c, pk, autosettings.wtimeouts)
		if err != nil {
			c.Close()
			Logi(LOG_KERL, " socket auth ", p.addr, " sendfail ", err)
			return
		}

		err = nil
		var xy XY = 0
		var pack []byte = nil
		dlen, err := recvPackageHeader(c, p.gethdlen(), autosettings.rtimeouts, p.headertemp)
		if err != nil {
			c.Close()
			Logi(LOG_KERL, " socket auth ", p.addr, " recvlenfail ", err)
			return
		}
		if true {
			if dlen < uint32(unsafe.Sizeof(xy)) {
				c.Close()
				Logi(LOG_KERL, " socket auth ", p.addr, " XY limit ")
				return
			}
		}
		if dlen > 0 {
			pack = allocPack(dlen)
			err = loopRecv(c, pack, autosettings.rtimeouts)
			if err != nil {
				c.Close()
				Logi(LOG_KERL, " socket auth ", p.addr, " recvfail ", err)
				return
			}
		}
		if true {
			if dlen < uint32(unsafe.Sizeof(xy)) {
				c.Close()
				Logi(LOG_KERL, " socket auth ", p.addr, " XY limit ")
				return
			}
			io := bytes.NewBuffer(pack)
			binary.Read(io, binary.LittleEndian, &xy)
		}
		dpack := pack[int(unsafe.Sizeof(xy)):]
		var sid int64
		var errori int64
		if err != nil {
			c.Close()
			Logi(LOG_KERL, " socket auth ", p.addr, " recv fail:", err)
			return
		}
		if xy != 30002 || dpack == nil || len(dpack) == 0 {
			c.Close()
			Logi(LOG_KERL, " socket auth ", p.addr, " Data err")
			return
		} else {
			jr := NewJSON()
			err = json.Unmarshal(dpack, &jr)
			if err != nil {
				c.Close()
				Logi(LOG_KERL, " socket auth ", p.addr, " Json err")
				return
			} else {
				jrd := JSONRead{
					JSONT: jr,
				}
				sid = jrd.Num("id")
				errori = jrd.Num("error")
				if sid == 0 || errori != 0 {
					c.Close()
					Logi(LOG_KERL, " socket auth ", p.addr, " ret serverid err")
					return
				}
			}
		}
	}
	p.con = c
	return
}
